// $codepro.audit.disable logExceptions
/*
 *  PreferenesWindow.java 
 *    
 *    Copyright 2012 YAWP Project Team
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package com.googlecode.yawp.view;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;

import java.awt.GridBagLayout;
import java.awt.GridBagConstraints;
import java.awt.Insets;
import javax.swing.JLabel;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.text.DecimalFormat;
import java.util.Observable;
import java.util.Observer;

import javax.swing.ButtonGroup;
import javax.swing.JTextField;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JCheckBox;



import com.googlecode.yawp.model.DataEntry;
import com.googlecode.yawp.model.Model;
import com.googlecode.yawp.model.Preferences;
import com.googlecode.yawp.model.Preferences.Units;
import com.googlecode.yawp.model.Preferences.DataView;

/**
 * Display the Users Preferences
 * 
 * @author ccato
 * @author Dan Spuches
 *
 */
@SuppressWarnings("serial")
public class PreferencesWindow extends JFrame implements Observer{

	private JPanel contentPane;
	private JTextField tfName;
	private JRadioButton rdbtnUs;
	private JRadioButton rdbtnMetric;
	private ButtonGroup bgUnitsGroup;
	private JTextField tfTargetWeight;
	private JTextField tfHeight;
	private JRadioButton rdbtnDaily;
	private JRadioButton rdbtnWeekly;
	private JRadioButton rdbtnMonthly;
	private ButtonGroup bgDataViewGroup;
	private JCheckBox chckbxEnabled;
	private JButton btnOkButton;
	private JButton btnCancel;
	private JLabel lblWeightUnits;
	private JLabel lblHeightUnits;
	private boolean usUnitsSelected = false;
	private boolean metricUnitsSelected = false;
	
	private Model model;
	private Preferences preferences;


	/**
	 * Constructor - Create the frame.
	 */
	public PreferencesWindow(Model model) {
		this.model = model;
		initialize();
	}

	/**
	 * Initialize the window
	 */
	public void initialize() {
		
		setTitle("YAWP - Preferences");
		setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		setBounds(100, 100, 450, 500);
		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		setContentPane(contentPane);
		GridBagLayout gbl_contentPane = new GridBagLayout();
		gbl_contentPane.columnWidths = new int[]{0, 0, 0};
		gbl_contentPane.rowHeights = new int[]{0, 0, 0, 0, 0, 150, 0, 0, 0};
		gbl_contentPane.columnWeights = new double[]{1.0, 1.0, Double.MIN_VALUE};
		gbl_contentPane.rowWeights = new double[]{1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, Double.MIN_VALUE};
		contentPane.setLayout(gbl_contentPane);
		
		JPanel panel = new JPanel();
		GridBagConstraints gbc_panel = new GridBagConstraints();
		gbc_panel.insets = new Insets(0, 0, 5, 5);
		gbc_panel.fill = GridBagConstraints.BOTH;
		gbc_panel.gridx = 0;
		gbc_panel.gridy = 0;
		contentPane.add(panel, gbc_panel);
		//
		//////////////////////	
		// start of user name
		JLabel lblUserName = new JLabel("User Name");
		panel.add(lblUserName);
		
		JPanel panel_1 = new JPanel();
		GridBagConstraints gbc_panel_1 = new GridBagConstraints();
		gbc_panel_1.insets = new Insets(0, 0, 5, 0);
		gbc_panel_1.fill = GridBagConstraints.BOTH;
		gbc_panel_1.gridx = 1;
		gbc_panel_1.gridy = 0;
		contentPane.add(panel_1, gbc_panel_1);
		
		tfName = new JTextField();
		panel_1.add(tfName);
		tfName.setColumns(10);
		
		// Set Current Name
		tfName.setText(model.getUserName());
		
		////////////////////////
		// Start of Units
		JPanel panel_2 = new JPanel();
		GridBagConstraints gbc_panel_2 = new GridBagConstraints();
		gbc_panel_2.insets = new Insets(0, 0, 5, 5);
		gbc_panel_2.fill = GridBagConstraints.BOTH;
		gbc_panel_2.gridx = 0;
		gbc_panel_2.gridy = 1;
		contentPane.add(panel_2, gbc_panel_2);
		
		JLabel lblUnits = new JLabel("Units");
		panel_2.add(lblUnits);
		
		JPanel panel_9 = new JPanel();
		GridBagConstraints gbc_panel_9 = new GridBagConstraints();
		gbc_panel_9.insets = new Insets(0, 0, 5, 0);
		gbc_panel_9.fill = GridBagConstraints.BOTH;
		gbc_panel_9.gridx = 1;
		gbc_panel_9.gridy = 1;
		contentPane.add(panel_9, gbc_panel_9);
		
		rdbtnUs = new JRadioButton("US");
		panel_9.add(rdbtnUs);
		
		rdbtnMetric = new JRadioButton("Metric");
		panel_9.add(rdbtnMetric);
		
		bgUnitsGroup = new ButtonGroup();
		
		bgUnitsGroup.add(rdbtnUs);
		bgUnitsGroup.add(rdbtnMetric);
		
		// set units
		if (model.getUserUnits() == Units.US) {
			rdbtnUs.setSelected(true);
			usUnitsSelected = true;
			metricUnitsSelected = false;
			
		} else {
			rdbtnMetric.setSelected(true);
			usUnitsSelected = false;
			metricUnitsSelected = true;
		}
		
		rdbtnUs.addActionListener(new USUnitsActionListener());
		rdbtnMetric.addActionListener(new MetricUnitsActionListener());
		
		////////////////////////
		// Start of Target Weight
		JPanel panel_3 = new JPanel();
		GridBagConstraints gbc_panel_3 = new GridBagConstraints();
		gbc_panel_3.insets = new Insets(0, 0, 5, 5);
		gbc_panel_3.fill = GridBagConstraints.BOTH;
		gbc_panel_3.gridx = 0;
		gbc_panel_3.gridy = 2;
		contentPane.add(panel_3, gbc_panel_3);
		
		JLabel lblTargetWeight = new JLabel("Target Weight");
		panel_3.add(lblTargetWeight);
		
		JPanel panel_10 = new JPanel();
		GridBagConstraints gbc_panel_10 = new GridBagConstraints();
		gbc_panel_10.insets = new Insets(0, 0, 5, 0);
		gbc_panel_10.fill = GridBagConstraints.BOTH;
		gbc_panel_10.gridx = 1;
		gbc_panel_10.gridy = 2;
		contentPane.add(panel_10, gbc_panel_10);
		
		lblWeightUnits = new JLabel(Preferences.getWeightUnitName(model.getUserUnits()));
		tfTargetWeight = new JTextField();
		panel_10.add(tfTargetWeight);
		panel_10.add(lblWeightUnits);
		tfTargetWeight.setColumns(10);

		// Set Current Target Weight
		tfTargetWeight.setText(Double.toString(model.getUserTargetWeight()));
		
		////////////////////////
		// Start of Height
		JPanel panel_4 = new JPanel();
		GridBagConstraints gbc_panel_4 = new GridBagConstraints();
		gbc_panel_4.insets = new Insets(0, 0, 5, 5);
		gbc_panel_4.fill = GridBagConstraints.BOTH;
		gbc_panel_4.gridx = 0;
		gbc_panel_4.gridy = 3;
		contentPane.add(panel_4, gbc_panel_4);
		
		JLabel lblHeight = new JLabel("Height");
		panel_4.add(lblHeight);
		
		JPanel panel_11 = new JPanel();
		GridBagConstraints gbc_panel_11 = new GridBagConstraints();
		gbc_panel_11.insets = new Insets(0, 0, 5, 0);
		gbc_panel_11.fill = GridBagConstraints.BOTH;
		gbc_panel_11.gridx = 1;
		gbc_panel_11.gridy = 3;
		contentPane.add(panel_11, gbc_panel_11);
		
		lblHeightUnits = new JLabel(Preferences.getHeightUnitName(model.getUserUnits()));
		tfHeight = new JTextField();
		panel_11.add(tfHeight);
		panel_11.add(lblHeightUnits);
		tfHeight.setColumns(10);

		// Set Current Height
		tfHeight.setText(Double.toString(model.getUserHeight()));
		
		////////////////////////
		// Start of Auto Fill
		JPanel panel_5 = new JPanel();
		GridBagConstraints gbc_panel_5 = new GridBagConstraints();
		gbc_panel_5.insets = new Insets(0, 0, 5, 5);
		gbc_panel_5.fill = GridBagConstraints.BOTH;
		gbc_panel_5.gridx = 0;
		gbc_panel_5.gridy = 4;
		contentPane.add(panel_5, gbc_panel_5);
		
		JLabel lblAutofillBlanks = new JLabel("Auto-fill Blanks");
		panel_5.add(lblAutofillBlanks);
		
		JPanel panel_12 = new JPanel();
		GridBagConstraints gbc_panel_12 = new GridBagConstraints();
		gbc_panel_12.insets = new Insets(0, 0, 5, 0);
		gbc_panel_12.fill = GridBagConstraints.BOTH;
		gbc_panel_12.gridx = 1;
		gbc_panel_12.gridy = 4;
		contentPane.add(panel_12, gbc_panel_12);
		
		chckbxEnabled = new JCheckBox("Enabled");
		panel_12.add(chckbxEnabled);
		
		// set auto fill
		chckbxEnabled.setSelected(model.getUserAutoFill());
		
		//TODO enable when feature is implemented
		chckbxEnabled.setEnabled(false);
		lblAutofillBlanks.setEnabled(false);
		
		////////////////////////
		// Start of Journal Categories
		JPanel panel_6 = new JPanel();
		GridBagConstraints gbc_panel_6 = new GridBagConstraints();
		gbc_panel_6.insets = new Insets(0, 0, 5, 5);
		gbc_panel_6.fill = GridBagConstraints.BOTH;
		gbc_panel_6.gridx = 0;
		gbc_panel_6.gridy = 5;
		contentPane.add(panel_6, gbc_panel_6);
		
		JLabel lblJournalCategories = new JLabel("Journal Categories");
		panel_6.add(lblJournalCategories);
		
		JScrollPane scrollPane = new JScrollPane();
		GridBagConstraints gbc_scrollPane = new GridBagConstraints();
		gbc_scrollPane.insets = new Insets(0, 0, 5, 0);
		gbc_scrollPane.fill = GridBagConstraints.BOTH;
		gbc_scrollPane.gridx = 1;
		gbc_scrollPane.gridy = 5;
		contentPane.add(scrollPane, gbc_scrollPane);
		
		JTextArea textArea = new JTextArea();
		textArea.setRows(10);
		scrollPane.setViewportView(textArea);
		
		// Journal Categories are currently disabled
		// TODO Optional: add Journal Categories Preferences support
		textArea.setEditable(false);
		lblJournalCategories.setEnabled(false);
		
		////////////////////////
		// Start of Data View 
		JPanel panel_7 = new JPanel();
		GridBagConstraints gbc_panel_7 = new GridBagConstraints();
		gbc_panel_7.insets = new Insets(0, 0, 5, 5);
		gbc_panel_7.fill = GridBagConstraints.BOTH;
		gbc_panel_7.gridx = 0;
		gbc_panel_7.gridy = 6;
		contentPane.add(panel_7, gbc_panel_7);
		
		JLabel lblDataView = new JLabel("Data View");
		panel_7.add(lblDataView);
		
		JPanel panel_13 = new JPanel();
		GridBagConstraints gbc_panel_13 = new GridBagConstraints();
		gbc_panel_13.insets = new Insets(0, 0, 5, 0);
		gbc_panel_13.fill = GridBagConstraints.BOTH;
		gbc_panel_13.gridx = 1;
		gbc_panel_13.gridy = 6;
		contentPane.add(panel_13, gbc_panel_13);
		
		rdbtnDaily = new JRadioButton("Daily");
		panel_13.add(rdbtnDaily);
		
		rdbtnWeekly = new JRadioButton("Weekly");
		panel_13.add(rdbtnWeekly);
		
		rdbtnMonthly = new JRadioButton("Monthly");
		panel_13.add(rdbtnMonthly);
		
		bgDataViewGroup = new ButtonGroup();
		bgDataViewGroup.add(rdbtnDaily);
		bgDataViewGroup.add(rdbtnWeekly);
		bgDataViewGroup.add(rdbtnMonthly);
		
		
		// set default view
		switch(model.getUserDataView()) {
			case WEEKLY: 
				rdbtnWeekly.setSelected(true);
				break;
			case MONTHLY:
				rdbtnMonthly.setSelected(true);
				break;
			case DAILY:
				rdbtnDaily.setSelected(true);
				break;
			default:
				System.out.println("Invalid data view!!");
		}
		
		// TODO implement this feature
		lblDataView.setEnabled(false);
		rdbtnDaily.setEnabled(false);
		rdbtnWeekly.setEnabled(false);
		rdbtnMonthly.setEnabled(false);
		
		
		////////////////////////
		// Start of OK Cancel Buttons
		JPanel panel_8 = new JPanel();
		GridBagConstraints gbc_panel_8 = new GridBagConstraints();
		gbc_panel_8.gridwidth = 2;
		gbc_panel_8.insets = new Insets(0, 0, 0, 5);
		gbc_panel_8.fill = GridBagConstraints.BOTH;
		gbc_panel_8.gridx = 0;
		gbc_panel_8.gridy = 7;
		contentPane.add(panel_8, gbc_panel_8);
		
		btnOkButton = new JButton("OK");
		panel_8.add(btnOkButton);
		
		btnCancel = new JButton("Cancel");
		panel_8.add(btnCancel);
	}

	/**
	 * Hook for view to register listener for the OK button
	 * 
	 * @param listener The ActionListener to assign to the button
	 */
	public void addPreferencesOKListener(ActionListener listener) {
		btnOkButton.addActionListener(listener);
	}

	/**
	 * Hook for view to register listener for the OK button
	 * 
	 * @param listener The ActionListener to assign to the button
	 */
	public void addPreferencesCancelListener(ActionListener listener) {
		btnCancel.addActionListener(listener);
	}
	
	/**
	 * Observer Update
	 */
	@Override
	public void update(Observable arg0, Object arg1) {
		// TODO maybe the preferences might need updating as a future feature
	}

	/**
	 * This function validates that the values in the PreferencesWindow entered by the user
	 * are valid
	 * 
	 * @return True if all preferences are valid, false if any are invalid
	 */
	public boolean validatePreferences() {
		// validate name field
		
		// validate target weight field
		String value = tfTargetWeight.getText();
		try {
			double d = Double.parseDouble(value);
			if (d < 0) {
				View.displayErrorMessage("Target weight cannot be negative.");
				return false;
			}
		} catch (NumberFormatException e) {
			View.displayErrorMessage(value + " is not a valid numeric value.");
			tfTargetWeight.requestFocusInWindow();
			return false;
		}
		
		
		
		
		// validate height field
		value = tfHeight.getText();
		try {
			double d = Double.parseDouble(value);
			if (d < 0) {
				View.displayErrorMessage("Height cannot be negative.");
				return false;
			}
		} catch (NumberFormatException e) {
			View.displayErrorMessage(value + " is not a valid numeric value.");
			tfHeight.requestFocusInWindow();
			return false;
		}
		
		return true;
	}
	
	/** 
	 * Helper function to get the users preference after OK selected
	 * 
	 * @return Preference of the user
	 */
	public Preferences getPreferences() {
		String name;
		Units units;
		double targetWeight;
		double height;
		boolean autoFill;
		// journalCategories
		DataView dataView;
		
		// name
		name = tfName.getText();
		
		// units
		if(rdbtnUs.isSelected()) {
			units = Units.US;	// US
		} else {
			units = Units.METRIC; 	// metric
		}
		
		// target weight
		targetWeight = Double.valueOf(tfTargetWeight.getText());
		
		// height
		height = Double.valueOf(tfHeight.getText());
		
		// auto fill
		autoFill = chckbxEnabled.isSelected();
		
		// journal categories 
		// TODO Optional Journal Categories update
		
		// default data view
		if(rdbtnDaily.isSelected()) {
			dataView = DataView.DAILY;
		} else if(rdbtnWeekly.isSelected()) {
			dataView = DataView.WEEKLY;
		} else {
			dataView = DataView.MONTHLY;
		}
		
		return new Preferences(name, units, targetWeight, height, autoFill, null, dataView);
	}
	
	
	/**
	 * This private inner class listens for clicks on the metric units radio button.
	 * When clicked, it will update the labels and convert the units for target 
	 * weight and height.
	 * 
	 * @author Dan Spuches
	 *
	 */
	private class MetricUnitsActionListener implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent e) {
			
			// only make changes if the units are changing
			if (usUnitsSelected) {
				
				// update labels
				lblHeightUnits.setText(Preferences.getHeightUnitName(Preferences.Units.METRIC));
				lblWeightUnits.setText(Preferences.getWeightUnitName(Preferences.Units.METRIC));
				
				// convert target weight value
				double targetWeight = Double.parseDouble(tfTargetWeight.getText());
				targetWeight = DataEntry.poundsToKilograms(targetWeight);
				DecimalFormat dFormat = new DecimalFormat("#.#");
		        targetWeight = Double.valueOf(dFormat.format(targetWeight));
				tfTargetWeight.setText(Double.toString(targetWeight));
				
				// convert height value
				double height = Double.parseDouble(tfHeight.getText());
				height = DataEntry.inchesToCentimeters(height);
		        height = Double.valueOf(dFormat.format(height));
				tfHeight.setText(Double.toString(height));
				
				// toggle selected flags
				usUnitsSelected = false;
				metricUnitsSelected = true;
			}
		}
		
	}
	
	/**
	 * This private inner class listens for clicks on the US units radio button.
	 * When clicked, it will update the labels and convert the units for target 
	 * weight and height.
	 * 
	 * @author Dan Spuches
	 *
	 */
	private class USUnitsActionListener implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent e) {
			
			// only make changes if the units are changing
			if (metricUnitsSelected) {

				// update labels
				lblHeightUnits.setText(Preferences.getHeightUnitName(Preferences.Units.US));
				lblWeightUnits.setText(Preferences.getWeightUnitName(Preferences.Units.US));
				
				// convert target weight values
				double targetWeight = Double.parseDouble(tfTargetWeight.getText());
				targetWeight = DataEntry.kilogramsToPounds(targetWeight);
				DecimalFormat dFormat = new DecimalFormat("#.#");
		        targetWeight = Double.valueOf(dFormat.format(targetWeight));
				tfTargetWeight.setText(Double.toString(targetWeight));
				
				// convert height values
				double height = Double.parseDouble(tfHeight.getText());
				height = DataEntry.centimetersToInches(height);
		        height = Double.valueOf(dFormat.format(height));
				tfHeight.setText(Double.toString(height));
				
				// toggle selecte flags
				usUnitsSelected = true;
				metricUnitsSelected = false;
			}
		}
		
	}
	
}
